# -*- coding: utf-8 -*-
import binascii
import base64
import requests
from bs4 import BeautifulSoup
import rsa
from urllib import parse
import json
import time
import random
from flask import Blueprint, request, jsonify
from flask_cors import cross_origin

evaluation_bp = Blueprint('evaluation', __name__)


class Login(object):
    def __init__(self, base_url):
        self.base_url = base_url
        self.key_url = parse.urljoin(base_url, '/xtgl/login_getPublicKey.html')
        self.login_url = parse.urljoin(base_url, '/xtgl/login_slogin.html')
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
            'Referer': self.login_url
        }
        self.sess = requests.Session()
        self.cookies = ''
        self.cookies_str = ''

    def login(self, sid, password):
        try:
            # 1. 获取登录页面和CSRF令牌
            req = self.sess.get(self.login_url, headers=self.headers)
            req.raise_for_status()
            soup = BeautifulSoup(req.text, 'html.parser')

            # 更可靠的CSRF令牌获取方式
            token_element = soup.find('input', {'name': 'csrftoken'})
            if not token_element:
                return False, "无法获取CSRF令牌，检查页面元素"
            tokens = token_element.get("value", "")

            # 2. 获取RSA公钥
            res = self.sess.get(self.key_url, headers=self.headers)
            res.raise_for_status()
            key_data = res.json()
            n = key_data['modulus']
            e = key_data['exponent']  # 公钥参数

            # 3. 加密密码 - 使用Crypto库统一处理
            hmm = self.get_rsa(password, n, e)  # RSA加密密码
            if not hmm:
                return False, "密码加密失败"

            # 4. 提交登录请求
            login_data = {
                'csrftoken': tokens,
                'yhm': sid,
                'mm': hmm
            }
            response = self.sess.post(self.login_url, headers=self.headers, data=login_data)
            response.raise_for_status()

            # 5. 检查登录结果
            if "用户名或密码不正确" in response.text or "密码错误" in response.text:
                return False, "账号或密码错误，请重新输入！"

            # 6. 登录成功处理
            self.cookies = self.sess.cookies
            self.cookies_str = '; '.join([f"{item.name}={item.value}" for item in self.cookies])
            return True, "登录成功"

        except requests.exceptions.RequestException as e:
            return False, f"网络请求失败: {str(e)}"
        except KeyError as e:
            return False, f"JSON解析失败，缺少关键字段: {str(e)}"
        except Exception as e:
            return False, f"发生未知错误: {str(e)}"

    @classmethod
    def get_rsa(cls, pwd, n, e):
        """使用rsa库进行RSA加密"""
        try:
            # 解码Base64格式的公钥参数
            rsa_n = base64.b64decode(n)
            rsa_e = base64.b64decode(e)

            # 构建RSA公钥
            n_int = int.from_bytes(rsa_n, 'big')
            e_int = int.from_bytes(rsa_e, 'big')
            
            pub_key = rsa.PublicKey(n_int, e_int)

            # 进行加密
            encrypted = rsa.encrypt(pwd.encode('utf-8'), pub_key)
            return base64.b64encode(encrypted).decode('utf-8')

        except Exception as e:
            return None


def fetch_course_ids(user_cookie, base_url):
    """获取所有待评价课程的ID信息"""
    url = parse.urljoin(base_url, '/xspjgl/xspj_cxXspjIndex.html?doType=query&gnmkdm=N401605')
    payload_template = {
        '_search': "false",
        'nd': str(int(time.time() * 1000)),
        'queryModel.showCount': "100",  # 每页获取更多数据减少请求次数
        'queryModel.sortName': "kcmc,jzgmc",
        'queryModel.sortOrder': "asc",
        'time': "1"
    }
    headers = {
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0",
        'Cookie': user_cookie,
        'X-Requested-With': "XMLHttpRequest",
        'Referer': parse.urljoin(base_url, '/xspjgl/xspj_cxXspjIndex.html?doType=details&gnmkdm=N401605&layout=default')
    }

    all_courses = []
    current_page = 1
    total_pages = 1

    while current_page <= total_pages:
        payload = payload_template.copy()
        payload['queryModel.currentPage'] = str(current_page)

        try:
            response = requests.post(url, data=payload, headers=headers)
            response.raise_for_status()
            data = response.json()

            if 'items' not in data or not data['items']:
                break

            # 提取关键ID
            for item in data['items']:
                course_info = (
                    item.get('jxb_id'),
                    item.get('kch_id'),
                    item.get('jgh_id')
                )
                if all(course_info):
                    all_courses.append(course_info)

            total_pages = int(data.get('totalPage', 1))
            current_page += 1
            time.sleep(0.8 + random.random())  # 随机请求间隔防止封禁

        except Exception as e:
            break

    return all_courses


def submit_evaluations(user_cookie, course_list, base_url):
    """提交课程评价"""
    if not course_list:
        return 0, "无有效课程数据，跳过评价阶段"

    url = parse.urljoin(base_url, "/xspjgl/xspj_tjXspj.html?gnmkdm=N401605")

    # 完整评价payload模板
    base_payload = {
            'ztpjbl': "100",
            'jszdpjbl': "0",
            'xykzpjbl': "0",
            'xsdm': "01",
            'modelList[0].pjmbmcb_id': "36B7435365E51BF4E065000000000001",  # 评价模板ID（可能需调整）
            'modelList[0].pjdxdm': "01",
            'modelList[0].fxzgf': "",
            'modelList[0].pjzt': "1",
            'tjzt': "1",
            'modelList[0].xspjList[0].childXspjList[0].pjf': "99",
            'modelList[0].xspjList[0].childXspjList[0].pjzbxm_id': "36B7435365EC1BF4E065000000000001",
            'modelList[0].xspjList[0].childXspjList[0].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[0].childXspjList[0].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[0].childXspjList[1].pjf': "99",
            'modelList[0].xspjList[0].childXspjList[1].pjzbxm_id': "36B7435365ED1BF4E065000000000001",
            'modelList[0].xspjList[0].childXspjList[1].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[0].childXspjList[1].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[0].childXspjList[2].pjf': "99",
            'modelList[0].xspjList[0].childXspjList[2].pjzbxm_id': "36B7435365EE1BF4E065000000000001",
            'modelList[0].xspjList[0].childXspjList[2].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[0].childXspjList[2].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[0].pjzbxm_id': "36B7435365E61BF4E065000000000001",
            'modelList[0].xspjList[1].childXspjList[0].pjf': "99",
            'modelList[0].xspjList[1].childXspjList[0].pjzbxm_id': "36B7435365EF1BF4E065000000000001",
            'modelList[0].xspjList[1].childXspjList[0].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[1].childXspjList[0].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[1].childXspjList[1].pjf': "99",
            'modelList[0].xspjList[1].childXspjList[1].pjzbxm_id': "36B7435365F01BF4E065000000000001",
            'modelList[0].xspjList[1].childXspjList[1].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[1].childXspjList[1].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[1].childXspjList[2].pjf': "99",
            'modelList[0].xspjList[1].childXspjList[2].pjzbxm_id': "36B7435365F11BF4E065000000000001",
            'modelList[0].xspjList[1].childXspjList[2].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[1].childXspjList[2].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[1].childXspjList[3].pjf': "99",
            'modelList[0].xspjList[1].childXspjList[3].pjzbxm_id': "36B7435365F21BF4E065000000000001",
            'modelList[0].xspjList[1].childXspjList[3].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[1].childXspjList[3].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[1].pjzbxm_id': "36B7435365E71BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[0].pjf': "99",
            'modelList[0].xspjList[2].childXspjList[0].pjzbxm_id': "36B7435365F31BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[0].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[2].childXspjList[0].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[1].pjf': "99",
            'modelList[0].xspjList[2].childXspjList[1].pjzbxm_id': "36B7435365F41BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[1].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[2].childXspjList[1].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[2].pjf': "99",
            'modelList[0].xspjList[2].childXspjList[2].pjzbxm_id': "36B7435365F51BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[2].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[2].childXspjList[2].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[3].pjf': "99",
            'modelList[0].xspjList[2].childXspjList[3].pjzbxm_id': "36B7435365F61BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[3].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[2].childXspjList[3].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[4].pjf': "99",
            'modelList[0].xspjList[2].childXspjList[4].pjzbxm_id': "36B7435365F71BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[4].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[2].childXspjList[4].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[5].pjf': "99",
            'modelList[0].xspjList[2].childXspjList[5].pjzbxm_id': "36B7435365F81BF4E065000000000001",
            'modelList[0].xspjList[2].childXspjList[5].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[2].childXspjList[5].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[2].pjzbxm_id': "36B7435365E81BF4E065000000000001",
            'modelList[0].xspjList[3].childXspjList[0].pjf': "99",
            'modelList[0].xspjList[3].childXspjList[0].pjzbxm_id': "36B7435365F91BF4E065000000000001",
            'modelList[0].xspjList[3].childXspjList[0].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[3].childXspjList[0].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[3].childXspjList[1].pjf': "99",
            'modelList[0].xspjList[3].childXspjList[1].pjzbxm_id': "36B7435365FA1BF4E065000000000001",
            'modelList[0].xspjList[3].childXspjList[1].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[3].childXspjList[1].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[3].childXspjList[2].pjf': "99",
            'modelList[0].xspjList[3].childXspjList[2].pjzbxm_id': "36B7435365FB1BF4E065000000000001",
            'modelList[0].xspjList[3].childXspjList[2].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[3].childXspjList[2].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[3].pjzbxm_id': "36B7435365E91BF4E065000000000001",
            'modelList[0].xspjList[4].childXspjList[0].pjf': "99",
            'modelList[0].xspjList[4].childXspjList[0].pjzbxm_id': "36B7435365FC1BF4E065000000000001",
            'modelList[0].xspjList[4].childXspjList[0].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[4].childXspjList[0].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[4].childXspjList[1].pjf': "99",
            'modelList[0].xspjList[4].childXspjList[1].pjzbxm_id': "36B7435365FD1BF4E065000000000001",
            'modelList[0].xspjList[4].childXspjList[1].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[4].childXspjList[1].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[4].childXspjList[2].pjf': "99",
            'modelList[0].xspjList[4].childXspjList[2].pjzbxm_id': "36B7435365FE1BF4E065000000000001",
            'modelList[0].xspjList[4].childXspjList[2].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[4].childXspjList[2].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[4].pjzbxm_id': "36B7435365EA1BF4E065000000000001",
            'modelList[0].xspjList[5].childXspjList[0].pjf': "99",
            'modelList[0].xspjList[5].childXspjList[0].pjzbxm_id': "36B7435365FF1BF4E065000000000001",
            'modelList[0].xspjList[5].childXspjList[0].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[5].childXspjList[0].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[5].childXspjList[1].pjf': "99",
            'modelList[0].xspjList[5].childXspjList[1].pjzbxm_id': "36B7435366001BF4E065000000000001",
            'modelList[0].xspjList[5].childXspjList[1].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[5].childXspjList[1].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[5].childXspjList[2].pjf': "99",
            'modelList[0].xspjList[5].childXspjList[2].pjzbxm_id': "36B7435366011BF4E065000000000001",
            'modelList[0].xspjList[5].childXspjList[2].pfdjdmb_id': "16E727009513564EE065000000000001",
            'modelList[0].xspjList[5].childXspjList[2].zsmbmcb_id': "36B7435365E51BF4E065000000000001",
            'modelList[0].xspjList[5].pjzbxm_id': "36B7435365EB1BF4E065000000000001",
        }

    headers = {
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0",
        'Cookie': user_cookie,
        'X-Requested-With': "XMLHttpRequest",
        'Origin': base_url,
        'Referer': parse.urljoin(base_url, "/xspjgl/xspj_cxXspjIndex.html?doType=details&gnmkdm=N401605&layout=default"),
    }

    success_count = 0
    for jxb_id, kch_id, jgh_id in course_list:
        payload = base_payload.copy()
        payload.update({
            'jxb_id': jxb_id,
            'kch_id': kch_id,
            'jgh_id': jgh_id,
            'modelList[0].jxb_id': jxb_id,
            'modelList[0].kch_id': kch_id,
            'modelList[0].jgh_id': jgh_id,
        })

        try:
            response = requests.post(url, data=payload, headers=headers)
            response.raise_for_status()
            result = response.json()

            if result.get('flag') == '1':
                success_count += 1

            time.sleep(1.5 + random.random())  # 防止请求过快

        except Exception as e:
            continue

    return success_count, f"成功提交{success_count}门课程的评价"


@evaluation_bp.route('/login', methods=['POST'])
@cross_origin()
def login_api():
    """登录API"""
    try:
        data = request.get_json()
        base_url = data.get('base_url')
        username = data.get('username')
        password = data.get('password')

        if not all([base_url, username, password]):
            return jsonify({
                'success': False,
                'message': '请填写完整的登录信息'
            }), 400

        # 创建登录实例并尝试登录
        login_instance = Login(base_url)
        success, message = login_instance.login(username, password)

        if success:
            return jsonify({
                'success': True,
                'message': message,
                'cookies': login_instance.cookies_str
            })
        else:
            return jsonify({
                'success': False,
                'message': message
            }), 401

    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'登录过程中发生错误: {str(e)}'
        }), 500


@evaluation_bp.route('/get_courses', methods=['POST'])
@cross_origin()
def get_courses_api():
    """获取课程列表API"""
    try:
        data = request.get_json()
        base_url = data.get('base_url')
        cookies = data.get('cookies')

        if not all([base_url, cookies]):
            return jsonify({
                'success': False,
                'message': '缺少必要参数'
            }), 400

        # 获取课程列表
        courses = fetch_course_ids(cookies, base_url)

        return jsonify({
            'success': True,
            'message': f'成功获取{len(courses)}门待评价课程',
            'course_count': len(courses),
            'courses': courses
        })

    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'获取课程列表时发生错误: {str(e)}'
        }), 500


@evaluation_bp.route('/submit_evaluations', methods=['POST'])
@cross_origin()
def submit_evaluations_api():
    """提交评价API"""
    try:
        data = request.get_json()
        base_url = data.get('base_url')
        cookies = data.get('cookies')
        courses = data.get('courses')

        if not all([base_url, cookies, courses]):
            return jsonify({
                'success': False,
                'message': '缺少必要参数'
            }), 400

        # 提交评价
        success_count, message = submit_evaluations(cookies, courses, base_url)

        return jsonify({
            'success': True,
            'message': message,
            'success_count': success_count,
            'total_count': len(courses)
        })

    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'提交评价时发生错误: {str(e)}'
        }), 500


@evaluation_bp.route('/auto_evaluate', methods=['POST'])
@cross_origin()
def auto_evaluate_api():
    """一键自动评价API"""
    try:
        data = request.get_json()
        base_url = data.get('base_url')
        username = data.get('username')
        password = data.get('password')

        if not all([base_url, username, password]):
            return jsonify({
                'success': False,
                'message': '请填写完整的登录信息'
            }), 400

        # 步骤1: 登录
        login_instance = Login(base_url)
        success, message = login_instance.login(username, password)
        
        if not success:
            return jsonify({
                'success': False,
                'message': f'登录失败: {message}'
            }), 401

        # 步骤2: 获取课程列表
        courses = fetch_course_ids(login_instance.cookies_str, base_url)
        
        if not courses:
            return jsonify({
                'success': False,
                'message': '未找到待评价课程'
            })

        # 步骤3: 提交评价
        success_count, eval_message = submit_evaluations(login_instance.cookies_str, courses, base_url)

        return jsonify({
            'success': True,
            'message': f'自动评价完成！{eval_message}',
            'course_count': len(courses),
            'success_count': success_count
        })

    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'自动评价过程中发生错误: {str(e)}'
        }), 500

